/*
 * Copyright (C) 2012-2013 c-netrunner (http://code.google.com/p/c-netrunner)
 * 
 * This program is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as published 
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *   
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.ridgelineapps.netrunner.core;

import java.util.Random;

import com.ridgelineapps.netrunner.ai.TestCorp;
import com.ridgelineapps.netrunner.ai.TestRunner;
import com.ridgelineapps.netrunner.api.Action;
import com.ridgelineapps.netrunner.api.Action.Type;
import com.ridgelineapps.netrunner.api.Card;
import com.ridgelineapps.netrunner.api.Corp;
import com.ridgelineapps.netrunner.api.GameContext;
import com.ridgelineapps.netrunner.api.PlayArea;
import com.ridgelineapps.netrunner.api.Runner;
import com.ridgelineapps.netrunner.api.TurnEvent;

//TODO: Trash cards when memory drops
public class Game
{
	Runner runner;
	Corp corp;

	PlayArea playArea;
	
	int turn;
	boolean gameOver;
	
	Random rand;
// GameUi ui;
	
	public void play()
	{
		rand = new Random();
		turn = 1;
		gameOver = false;
		playArea = new PlayArea(this);

		runner.init();
		corp.init();
		
		while (!gameOver /*&& !ui.getStopEarly()*/)
		{
         //////////////////////////////////
         // Corp's turn
         //////////////////////////////////
			playArea.corpClicks = 3;
			playArea.corpDrawCard();
			
         GameContext context = new GameContext(this, playArea, Card.Side.Corp);
			for(Card card : Utils.getCorpRezzedRemoteServers(playArea)) {
			   card.turnEvent(context, TurnEvent.begin);
	         if(checkGameOver()) {
	            break;
	         }
			}
			
			if(checkGameOver()) {
			   break;
			}
			
			while(playArea.corpClicks > 0) {
   			Action action = corp.getAction(context);
            if(!playAction(action, context)) {
               break;
            }
            if(checkGameOver()) {
               break;
            }
			}

         if(checkGameOver()) {
            break;
         }
         
         for(Card card : Utils.getCorpRezzedRemoteServers(playArea)) {
            card.turnEvent(context, TurnEvent.end);
            if(checkGameOver()) {
               break;
            }
         }
         
         if(checkGameOver()) {
            break;
         }
         
         //////////////////////////////////
         // Runner's turn
         //////////////////////////////////
         context = new GameContext(this, playArea, Card.Side.Runner);
			playArea.runnerClicks = 4;
			
         for(Card card : Utils.getRunnersRig(playArea)) {
            card.turnEvent(context, TurnEvent.begin);
            if(checkGameOver()) {
               break;
            }
         }
         
         if(checkGameOver()) {
            break;
         }
         
         while(playArea.corpClicks > 0) {
            Action action = runner.getAction(context);
            if(!playAction(action, context)) {
               break;
            }
            if(checkGameOver()) {
               break;
            }
         }
			
         if(checkGameOver()) {
            break;
         }
         
         for(Card card : Utils.getRunnersRig(playArea)) {
            card.turnEvent(context, TurnEvent.end);
            if(checkGameOver()) {
               break;
            }
         }
         
         if(checkGameOver()) {
            break;
         }
         
			turn++;
		}
	}

	private boolean playAction(Action action, GameContext context)
	{
		if (!isActionValid(context, action))
		{
		   //TODO Handle real player differently...
			action = getRandomAction(context);
		}
		
		if(action == null) {
		   return false;
		}

		//////////////////////////////////
		// Pay click cost
		//////////////////////////////////
      if(action.type == Type.corpPurgeViruses) {
         playArea.corpClicks -= 3;
      }
      else if(context.side == Card.Side.Corp) {
         playArea.corpClicks--;
      }
      else {
         playArea.runnerClicks--;         
      }

      //////////////////////////////////
      // Pay credit cost
      //////////////////////////////////
      if(action.type == Type.runnerDetag) {
         playArea.runnerCredits -= 2;
      }
      else if(action.type == Type.corpTrashResource) {
         playArea.corpCredits -= 2;
      }
      else if(action.type == Type.corpAdvance) {
         playArea.runnerCredits--;
      }
      else if(action.type == Type.runnerInstall) {
         playArea.runnerCredits -= action.card.cost;
      }
      else if(action.type == Type.runnerEvent) {
         playArea.runnerCredits -= action.card.cost;
      }

      //////////////////////////////////
      // Do action
      //////////////////////////////////
      if(action.type == Type.corpDraw) {
         playArea.corpDrawCard();
      }
      else if(action.type == Type.runnerDraw) {
         playArea.runnerDrawCard();
      }
      else if(action.type == Type.runnerEvent) {
         action.card.event(context);
      }
      else if(action.type == Type.corpGainCredit) {
         playArea.corpCredits++;
      }
      else if(action.type == Type.runnerGainCredit) {
         playArea.runnerCredits++;
      }
      else if(action.type == Type.runnerDetag) {
         playArea.runnerTags--;
      }
      else if(action.type == Type.corpAdvance) {
         //TODO...
      }
      
		return true;
	}
	
	public Action getRandomAction(GameContext context)
	{
		return null;
	}
	
	public boolean isActionValid(GameContext context, Action action)
	{
	   //TODO "null" actions allowed? Does this meant to end turn early?
	   if(action == null) {
	      return true;
	   }
	   
	   if(context.side == Card.Side.Corp) {
         if (action.type == Type.runnerDetag || action.type == Type.runnerDraw || action.type == Type.runnerEvent || action.type == Type.runnerGainCredit
               || action.type == Type.runnerInstall || action.type == Type.runnerRun || action.type == Type.runnerTrigger) {
            System.out.println("Error::Wrong action for type of player (corp), action=" + action.type);
            return false;
         }
	   }
      if(context.side == Card.Side.Runner) {
         if (action.type == Type.corpAdvance || action.type == Type.corpDraw || action.type == Type.corpGainCredit || action.type == Type.corpInstall
               || action.type == Type.corpOperation || action.type == Type.corpPurgeViruses || action.type == Type.corpTrashResource
               || action.type == Type.corpTrigger) {
            System.out.println("Error::Wrong action for type of player (runner), action=" + action.type);
            return false;
         }
      }
      
      if(action.type == Type.corpPurgeViruses && playArea.corpClicks < 3) {
         System.out.println("Error::Not enough clicks to purge viruses");
         return false;
      }
      if(action.type == Type.corpAdvance && playArea.corpCredits < 1) {
         System.out.println("Error::Not enough credits to advance");
         return false;
      }
      if(action.type == Type.corpTrashResource && playArea.corpCredits < 2) {
         System.out.println("Error::Not enough credits to trash resource");
         return false;
      }
      if(action.type == Type.corpTrashResource && playArea.runnerTags == 0) {
         System.out.println("Error::Cannot trash resource because runner is not tagged");
         return false;
      }
      if(action.type == Type.runnerDetag && playArea.runnerCredits < 2) {
         System.out.println("Error::Not enough credits to remove tag");
         return false;
      }
      if(action.type == Type.runnerDetag && playArea.runnerTags == 0) {
         System.out.println("Error::Cannot remove tag because runner is not tagged");
         return false;
      }
      if(action.type == Type.runnerInstall) {
         if(action.card == null) {
            System.out.println("Error::No card specified on install");
            return false;
         }
         if(action.card.cost > playArea.runnerCredits) {
            System.out.println("Error::Not enough credits to install card");
            return false;               
         }
         if(action.card.type == Card.Type.Program) {
            if(playArea.calculateMemoryAvail() < playArea.calculateMemoryInUse() + action.card.memoryCost) {
               System.out.println("Error::Not enough memory available to install card");
               return false;               
            }
         }
      }
      if(action.type == Type.runnerEvent) {
         if(action.card == null) {
            System.out.println("Error::No card specified for event");
            return false;
         }
         if(action.card.cost > playArea.runnerCredits) {
            System.out.println("Error::Not enough credits to play event card");
            return false;               
         }
      }
      if(action.type == Type.corpInstall) {
         if(action.card == null) {
            System.out.println("Error::No card specified on install");
            return false;
         }
      }
         
		return true;
	}
	
	public boolean checkGameOver()
	{
	   if(gameOver) {
	      return true;
	   }
	   
	   return gameOver;
	}
	
	public static void main(String[] args)
	{
	   Game game = new Game();
	   game.runner = new TestRunner();
      game.corp = new TestCorp();
      game.play();
	}
}
